Reduced Instruction Set Computer (RISC) architecture focuses on the features needed to support static languages, particularly C. Static languages have data types which are known at compile time, and any runtime storage management is done explicitly by the programmer. Dynamic languages, such as Smalltalk and LISP, have data types which may not be known until runtime, and storage management, i.e. garbage collection, is done implicitly. The distinctions between static and dynamic languages become blurred as enhanced static languages, such as C++, acquire more characteristics of dynamic languages.
RISC architectures are well suited to the majority of dynamic language operations. Both LISP and Smalltalk are dominated by data movement, function calling and integer arithmetic. Typical RISC architectures are optimized for those operations. Dynamic languages, however, require support for possibilities rather than the probabilities supported in static languages. For example, any given arithmetic operation in a dynamic language probably will have simple integer operands, but those operands may be any type. As a general rule, one does not know the types of operands at compile time in dynamic languages, therefore it is necessary to do extensive checking at runtime which can be expensive both in terms of time and code space.
Data movement in dynamic languages presents similar problems. An incremental garbage collection system utilizes a read barrier that is checked on every fetch from heap storage. Generational garbage collectors use a write barrier that is checked with every store onto the heap. The cost of inline code for a read barrier to implement incremental garbage collection on conventional processors has proven to be prohibitive. Inline checks are used for write barriers on conventional processors because store operations are generally much less frequent than read operations, on such processors but the checks required still cause significant performance degradation.
LISP machines use extensive special hardware and microcode to do checking and special case handling. RISC processors do not have microcode and basic RISC philosophy dictates that a careful evaluation of the cost of special hardware be undertaken before adding such hardware. Therefore, efficient implementation of trap handlers in LISP and other dynamic languages running on RISC processors requires different strategies than the approaches used on LISP machines.
One approach uses relatively small amounts of special additional hardware to detect "unusual" conditions and then traps to a handler for those conditions. While this seems to be an effective approach, it burdens the trap handling architecture of existing RISC systems beyond their design limits, and often results in inadequate performance characteristics, such as unacceptably reduced program execution speed.
Trap architectures for dynamic language systems have requirements that are significantly different than those for more conventional systems. The trap handler must be able to emulate some operation that is not directly supported by the system hardware. Trap handlers need to interface with garbage collectors and dynamic language level handlers. This means the trap handler must have ready access to the dynamic language environment.
Additionally the trap handler cannot accept some restrictions that are often placed on trap handlers. For example, a dynamic language trap handler must reference arbitrary objects in the language environment, which means it must be able to tolerate page faults, i.e. the trap handler must also be able to trap.
Since dynamic language traps occur at a much greater frequency than traditional system traps, the trap architecture must minimize the software runtime to get to the trap handler, determine what must be done, and do it.
Current trap architectures on conventional processors are not designed for handling the traps of a dynamic language. They are intended to handle errors, such as address faults, or conditions requiring large amounts of processing, such as page faults. Conventional trap architectures are inadequate for use with dynamic languages for three main reasons. First, all traps in conventional machines enter the kernel mode, so the user has little or no control over how the trap is handled. Second, there is insufficient support in conventional architectures for rapid trap execution. Third, conventional trap handlers cannot tolerate traps themselves.
Most existing trap architectures expect the kernel to handle all traps. The kernel is not the most efficient level for implementation of dynamic language trap handlers. The kernel is both over privileged and under privileged for this purpose. It is over privileged in the sense that it generally can perform operations users cannot, such as accessing protected memory. If the trap handler is emulating a user instruction, it needs to do so with the same access rights as the user program. The kernel is under privileged because trap handlers usually run at low levels within the kernel and often do not have access to higher level functions such as file systems or communications.
Additionally, different dynamic languages need different trap handlers. They have different encoding schemes, different data types, different semantics, and different garbage collectors. The kernel needs a different set of handlers for each implementation of each language and must switch handlers with each process change.
On another level, kernel, or system trap handlers, cause problems unless all the dynamic languages and the kernel are written and maintained by the same manufacturer. Kernel vendors are reluctant to put "strange" code into the kernel portion of their systems. Even then, the coordination of kernel changes with all the dynamic language changes seems quite overwhelming and impractical.
Some RISC architecture machines provide a much more flexible mechanism for handling traps. In these systems, a user program can "register" trap handlers with the kernel that will be used when a particular trap occurs. These tag trap handlers as they are sometimes called, are part of the user program and run in user mode. The kernel calls the registered tag trap handler after a trap occurs, passing information such as the decoded trapping instruction and its operands to the trap handler. When returning, the tag trap handler can return results as if it came from the trapping instruction, or it can cause the instruction to be re-executed.
This approach eliminates many of the functional difficulties associated with kernel resident trap handlers. Unfortunately, it does not adequately deal with performance problems. In fact, the additional interface requirements employed with tag trap handlers tend to aggravate the performance problems associated with dynamic language trap handling.